package cz.zcu.fav.rat.rest.controller;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

import cz.zcu.fav.rat.beans.Object;
import cz.zcu.fav.rat.beans.Period;
import cz.zcu.fav.rat.beans.Reservation;
import cz.zcu.fav.rat.beans.User;
import cz.zcu.fav.rat.dataaccess.service.ObjectService;
import cz.zcu.fav.rat.dataaccess.service.PeriodService;
import cz.zcu.fav.rat.dataaccess.service.UserService;
import cz.zcu.fav.rat.dataaccess.service.impl.SearchService;
import cz.zcu.fav.rat.dataaccess.service.impl.SearchType;
import cz.zcu.fav.rat.rest.beans.OccupationMapping;
import cz.zcu.fav.rat.rest.beans.OccupationsMapping;
import cz.zcu.fav.rat.rest.errorhandling.UnauthorizedAccessRestException;
import cz.zcu.fav.rat.rest.errorhandling.UnknownResourceException;
import cz.zcu.fav.rat.rest.factory.ObjectsMappingFactory;
import cz.zcu.fav.rat.rest.factory.ReservationsMappingFactory;
import cz.zcu.fav.rat.rest.utils.RestUtils;
import cz.zcu.fav.rat.security.beans.UserDetailsBean;
import cz.zcu.fav.rat.security.beans.UserSessionController;

@Controller
@RequestMapping("/rest/objects")
public class ObjectRestController
{

	@Autowired
	ObjectService objectService;

	@Autowired
	PeriodService periodService;

	@Autowired
	SearchService searchService;

	@Autowired
	UserService userService;

	@Autowired
	ReservationsMappingFactory reservationsMappingFactory;

	@Autowired
	ObjectsMappingFactory objectsMappingFactory;

	@Autowired
	Jaxb2Marshaller jaxb2Marshaller;

	private String XML_VIEW_NAME = "objectRestView";

	@RequestMapping(method = RequestMethod.GET, value = "/object")
	public ModelAndView getObject(
			@RequestParam(value = "id", required = true) Integer id)
	{
		Object obj = objectService.get(id);

		if (obj == null)
		{
			throw new UnknownResourceException("Object does not exist!");
		}

		Set<Object> objSet = new HashSet<Object>();
		objSet.add(obj);
		return new ModelAndView(XML_VIEW_NAME, "object",
				objectsMappingFactory.getMappedObject(objSet));
	}

	@RequestMapping(method = RequestMethod.GET, value = "/object/identifier")
	public ModelAndView getObjectByIdentifier(
			@RequestParam(value = "identifier", required = true) String identifier)
	{
		List<Object> objects = objectService
				.findAllObjectsByIdentifier(identifier);
		Set<Object> objSet = new HashSet<Object>();
		objSet.addAll(objects);
		return new ModelAndView(XML_VIEW_NAME, "object",
				objectsMappingFactory.getMappedObject(objSet));
	}
	
	@RequestMapping(method = RequestMethod.GET, value = "/object/id")
	public ModelAndView getObjectById(
			@RequestParam(value = "id", required = true) Integer id)
	{
		Object object = objectService.get(id);
		Set<Object> objSet = new HashSet<Object>();
		objSet.add(object);
		return new ModelAndView(XML_VIEW_NAME, "object",
				objectsMappingFactory.getMappedObject(objSet));
	}

	@RequestMapping(method = RequestMethod.GET, value = "/object/search")
	public ModelAndView searchObject(
			@RequestParam(value = "type", required = true) SearchType searchType,
			@RequestParam(value = "key", required = true) String key)
	{
		Set<Object> foundedObjects = new HashSet<Object>();

		foundedObjects.addAll(searchService.searchForObject(key, searchType));

		return new ModelAndView(XML_VIEW_NAME, "object",
				objectsMappingFactory.getMappedObject(foundedObjects));
	}

	@RequestMapping(method = RequestMethod.POST, value = "/session/object/pickup")
	public ModelAndView pickUpObject(
			@RequestParam(value = "id", required = true) Integer id)
			throws UnknownResourceException
	{

		Map<Reservation, Collection<Period>> returnedRes = new HashMap<Reservation, Collection<Period>>();

		User user = getLoggedUser();

		Period per = periodService.pickUp(id, user);
		if (per != null)
		{
			List<Period> perList = new ArrayList<Period>();
			perList.add(per);
			returnedRes.put(per.getReservation(), perList);
		}

		return new ModelAndView(XML_VIEW_NAME, "object",
				reservationsMappingFactory.getMappedObject(returnedRes));
	}

	@RequestMapping(method = RequestMethod.GET, value = "/object/reservations")
	public ModelAndView getObjectReservations(
			@RequestParam(value = "since", required = true) String since,
			@RequestParam(value = "to", required = false) String to,
			@RequestParam(value = "id", required = true) Integer id)
			throws UnknownResourceException
	{
		LocalDateTime fromDate = RestUtils.convertRestTimeToJodaTime(since);

		LocalDateTime toDate = RestUtils.convertRestTimeToJodaTime(to);

		List<Period> periods = periodService
				.getAllPeriodsByObjectIdInDateRange(id, fromDate, toDate);

		Multimap<Reservation, Period> multiMap = ArrayListMultimap.create();
		for (Period period : periods)
		{
			Reservation res = period.getReservation();
			multiMap.put(res, period);
		}

		Map<Reservation, Collection<Period>> reservationMap = multiMap.asMap();

		return new ModelAndView(XML_VIEW_NAME, "object",
				reservationsMappingFactory.getMappedObject(reservationMap));
	}

	@RequestMapping(method = RequestMethod.GET, value = "/object/reservations/occupation")
	public ModelAndView getObjectReservationsInMonth(
			@RequestParam(value = "id", required = true) Integer id,
			@RequestParam(value = "month", required = true) Integer month,
			@RequestParam(value = "year", required = true) Integer year)
	{
		LocalDate dt = new LocalDate(year, month, 1);
		LocalDate since = dt.dayOfMonth().withMinimumValue();
		LocalDate to = dt.dayOfMonth().withMaximumValue();

		Map<LocalDate, Collection<LocalDate>> dates = periodService.getOccupiedDaysByObject(id,
				since, to);
		
		OccupationsMapping mapping = new OccupationsMapping();
		for(Entry<LocalDate, Collection<LocalDate>> entry : dates.entrySet()) 
		{	
			OccupationMapping dateMapping = new OccupationMapping();
			dateMapping.setCount(entry.getValue().size());
			dateMapping.setDate(entry.getKey().toString());
			mapping.addDate(dateMapping);
		}

		return new ModelAndView(XML_VIEW_NAME, "object", mapping);
	}

	private User getLoggedUser() throws UnauthorizedAccessRestException
	{
		UserDetailsBean userBean = UserSessionController.getLoggedUserBean();
		Integer userId = userBean.getId();

		User user = userService.get(userId);
		if (user == null)
		{
			throw new UnknownResourceException("User with id "
					+ userBean.getUsername() + " was not found");
		}
		return user;
	}

}
